/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.inspur.edp.lcm.metadata.common;

import com.inspur.edp.lcm.metadata.api.service.FileService;
import com.inspur.edp.metadata.rtcustomization.api.exception.LcmFileException;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

class FileServiceImpTest {
    private final FileService fileService = new FileServiceImp();

    private static final String moduleDirName = "metadata-service-common";
    private static File resourceDir = new File("src/test/resources");
    private static File file = new File("src/test/resources/test.txt");
    private static File readFile = new File("src/test/resources/readFile.txt");
    private static File dir = new File("src/test/resources/test");
    private static File dest = new File("src/test/resources/dest");

    private static File zipFile = new File("src/test/resources/test.zip");
    private static File fileInDir = new File("src/test/resources/test/test.txt");

    @BeforeAll
    public static void before() throws IOException {
        if (!resourceDir.getCanonicalPath().contains(moduleDirName)) {
            resourceDir = Paths.get(moduleDirName, resourceDir.getPath()).toFile();
            file = Paths.get(moduleDirName, file.getPath()).toFile();
            readFile = Paths.get(moduleDirName, readFile.getPath()).toFile();
            dir = Paths.get(moduleDirName, dir.getPath()).toFile();
            dest = Paths.get(moduleDirName, dest.getPath()).toFile();
            zipFile = Paths.get(moduleDirName, zipFile.getPath()).toFile();
            fileInDir = Paths.get(moduleDirName, fileInDir.getPath()).toFile();
        }
        if (!resourceDir.exists()) {
            resourceDir.mkdirs();
        }
    }

    @AfterAll
    public static void after() throws IOException {
        if (file.exists()) {
            file.delete();
        }
        if (zipFile.exists()) {
            zipFile.delete();
        }
        if (fileInDir.exists()) {
            fileInDir.delete();
        }
        if (dir.exists()) {
            dir.delete();
        }
        FileServiceImp.cleanDirectory(resourceDir);
    }

    @Test
    void fileDelete() throws IOException {
        if (!file.exists()) {
            file.createNewFile();
        }
        assertTrue(file.exists());
        assertThrows(IOException.class, () -> fileService.fileDelete("x"));
        fileService.fileDelete(file.getPath());
        assertFalse(file.exists());
    }

    @Test
    void fileUpdate() throws Exception {
        if (!file.exists()) {
            file.createNewFile();
        }
        assertTrue(file.exists());
        long length = file.length();
        fileService.fileUpdate(file.getPath(), "1");
        assertEquals(file.length(), length + 1);

        // 在模拟的FileOutputStream上设定期望行为
//        PowerMockito.whenNew(FileOutputStream.class).withArguments("1", true).thenReturn(fosMock);

        // 设定模拟的FileOutputStream抛出IOException
//        Mockito.doThrow(IOException.class).when(fosMock).write(Mockito.any());

        // 执行被测试的方法
//        fileService.fileUpdate("1", "1");
        // TODO 未走进异常
        // 验证相关交互
//        Mockito.verify(fosMock).close();

    }

    @Test
    void testFileUpdate() throws IOException {
        if (!file.exists()) {
            file.createNewFile();
        }
        assertTrue(file.exists());
        long length = file.length();
        fileService.fileUpdate(file.getPath(), "1", true);
        assertEquals(file.length(), length + 1);
        fileService.fileUpdate(file.getPath(), "1", false);
        assertEquals(file.length(), 1);

        // TODO 未测试异常
    }

    @Test
    void isFileExist() throws IOException {
        if (!file.exists()) {
            file.createNewFile();
        }
        assertTrue(fileService.isFileExist(file.getPath()));
        file.delete();
        assertFalse(fileService.isFileExist(file.getPath()));
    }

    @Test
    void fileRename() throws Exception {
        if (!file.exists()) {
            file.createNewFile();
        }
        File newFile = new File(file.getPath() + "0");
        if (newFile.exists()) {
            newFile.delete();
        }
        fileService.fileRename(file.getPath(), newFile.getPath());
        assertFalse(file.exists());
        file.createNewFile();
        assertThrows(IOException.class, () -> fileService.fileRename(file.getPath(), newFile.getPath()));
        newFile.delete();
        fileService.fileRename(file.getPath(), null);
    }

    @Test
    void fileRead() throws IOException {
        if (file.exists(  )) {
            file.delete();
        }
        assertThrows(LcmFileException.class, () -> fileService.fileRead(file.getPath()));
        file.createNewFile();
        fileService.fileUpdate(file.getPath(), "\ufeff1");
        assertEquals("1", fileService.fileRead(file.getPath()));
    }

    @Test
    void fileCopy() throws IOException {
        if (!file.exists()) {
            file.createNewFile();
        }
        fileService.fileCopy(file.getPath(), file.getPath() + "1");
        assertTrue(new File(file.getPath() + "1").exists());
        new File(file.getPath() + "1").delete();
    }

    @Test
    void isDirectoryExist() {
        if (!dir.exists()) {
            dir.mkdirs();
        }
        assertTrue(fileService.isDirectoryExist(dir.getPath()));
        dir.delete();
    }

    @Test
    void getDirectorys() {
        if (dir.exists()) {
            dir.delete();
        }
        int size = fileService.getDirectorys(dir.getParent()).size();
        dir.mkdirs();

        assertEquals(size + 1, fileService.getDirectorys(dir.getParent()).size());
        dir.delete();
    }

    @Test
    void getAllFiles() throws IOException {
        if (file.exists()) {
            file.delete();
        }
        int size = fileService.getAllFiles(file.getParent()).size();
        if (!dir.exists()) {
            dir.mkdirs();
        }
        file.createNewFile();
        assertEquals(size + 1, fileService.getAllFiles(file.getParent()).size());
        dir.delete();
        file.delete();
    }

    @Test
    void testGetAllFiles() throws IOException {
        if (file.exists()) {
            file.delete();
        }
        int size = FileServiceImp.getAllFiles(file.getParent(), true).size();
        if (!dir.exists()) {
            dir.mkdirs();
        }
        file.createNewFile();
        assertEquals(size + 1, FileServiceImp.getAllFiles(file.getParent(), true).size());
        dir.delete();
        file.delete();
    }

    @Test
    void listAllFiles() throws IOException {
        if (file.exists()) {
            file.delete();
        }
        int size = FileServiceImp.listAllFiles(new File(file.getParent()), File::isFile).size();
        file.createNewFile();
        assertEquals(size + 1, FileServiceImp.listAllFiles(new File(file.getParent()), File::isFile).size());
        file.delete();
    }

    @Test
    void listAllFilesFilterDirAndFile() throws IOException {
        if (!file.exists()) {
            file.createNewFile();
        }
        assertEquals(1, FileServiceImp.listAllFilesFilterDirAndFile(new File(file.getParent()), (path, name) -> name.endsWith(".txt")).size());
        file.delete();
    }

    @Test
    void createDirectory() {
        if (dir.exists()) {
            dir.delete();
        }
        fileService.createDirectory(dir.getPath());
        assertTrue(dir.exists());
        dir.delete();
    }

    @Test
    void deleteAllFilesUnderDirectory() throws IOException {
        if (!dir.exists()) {
            dir.mkdirs();
        }
        if (!fileInDir.exists()) {
            fileInDir.createNewFile();
        }
        fileService.deleteAllFilesUnderDirectory(dir.getPath());
        assertFalse(fileInDir.exists());
        dir.delete();
    }

    @Test
    void cleanDirectory() throws IOException {
        if (!dir.exists()) {
            dir.mkdirs();
        }
        if (!fileInDir.exists()) {
            fileInDir.createNewFile();
        }
        FileServiceImp.cleanDirectory(dir);
        assertFalse(fileInDir.exists());
    }

    @Test
    void getCombinePath() {
        String a = "a";
        String b = "b";
        assertEquals("a/b", fileService.getCombinePath(a, b));
    }

    @Test
    void getExtension() {
        assertEquals(".txt", fileService.getExtension(file.getPath()));
    }

    @Test
    void testGetExtension() {
        if (!dir.exists()) {
            dir.mkdirs();
        }
        assertEquals(".txt", fileService.getExtension(file));
        assertThrows(IllegalArgumentException.class, () -> fileService.getExtension(dir));
        dir.delete();
    }

    @Test
    void getFileNameWithoutExtension() {
        assertEquals("test", fileService.getFileNameWithoutExtension(file.getPath()));
    }

    @Test
    void testGetFileNameWithoutExtension() {
        if (!dir.exists()) {
            dir.mkdirs();
        }
        assertEquals("test", fileService.getFileNameWithoutExtension(file));
        assertThrows(IllegalArgumentException.class, () -> fileService.getFileNameWithoutExtension(dir));
        dir.delete();
    }

    @Test
    void createFile() throws IOException {
        if (file.exists()) {
            file.delete();
        }
        fileService.createFile(file.getParent(), file.getName());
        assertTrue(file.exists());
        file.delete();
    }

    @Test
    void testCreateFile() throws IOException {
        if (file.exists()) {
            file.delete();
        }
        fileService.createFile(file.getPath());
        assertTrue(file.exists());
        file.delete();
    }

    @Test
    void getFileName() {
        assertEquals("test.txt", fileService.getFileName(file.getPath()));
    }

    @Test
    void getDirectoryName() {
        assertEquals("a", fileService.getDirectoryName("a/test.txt"));
    }

    @Test
    void getDirectorySeparatorChar() {
        assertEquals(File.separatorChar, fileService.getDirectorySeparatorChar());
    }

    @Test
    void deleteDirectory() throws IOException {
        assertThrows(IOException.class, () -> fileService.deleteDirectory(file.getPath()));
        if (!dir.exists()) {
            dir.mkdirs();
        }
        fileService.deleteDirectory(dir.getPath());
        assertFalse(dir.exists());
    }

    @Test
    void renameDirectory() throws IOException {
        if (!dir.exists()) {
            dir.mkdirs();
        }
        fileService.renameDirectory(dir.getPath(), dir.getPath() + "1");
        assertTrue(new File(dir.getPath() + "1").exists());
        assertThrows(IOException.class, () -> fileService.renameDirectory(dir.getPath(), dir.getPath() + "1"));
        new File(dir.getPath() + "1").delete();
    }

    @Test
    void forceMoveDir() throws IOException {
        if (!file.exists()) {
            file.createNewFile();
        }
        if (!dir.exists()) {
            dir.mkdirs();
        }
        fileService.forceMoveDir(file.getPath(), dir.getPath());
        fileInDir.delete();
    }


    @Test
    void getProjectPath() {
        assertNull(fileService.getProjectPath(dir.getPath()));
    }

    @Test
    void getJarPath() throws IOException {
        File javaDir = Paths.get(dir.getParent()).resolve("java").resolve("target").toFile();
        javaDir.mkdirs();
        File a = Paths.get(javaDir.getPath(), "a.jar").toFile();
        a.createNewFile();
        assertEquals(1, fileService.getJarPath(dir.getParent()).size());
        a.delete();
        javaDir.delete();
    }

    @Test
    void getApiModulePath() {
        File apiDir = Paths.get(dir.getParent()).resolve("java").resolve("s").resolve("api").toFile();
        apiDir.mkdirs();
        assertTrue(fileService.getApiModulePath(dir.getParent()).endsWith("api"));
        apiDir.delete();
        Paths.get(dir.getParent()).resolve("java").resolve("s").toFile().delete();
    }

    @Test
    void handlePath() {
        String a = "a" + File.separator + "b";
        assertEquals("a/b", FileServiceImp.handlePath(a));
    }

    @Test
    void getAllProjectsDirectories() throws IOException {
        if (!dir.exists()) {
            dir.mkdirs();
        }
        if (!fileInDir.exists()) {
            fileInDir.createNewFile();
        }
        List<String> projPaths = new ArrayList<>();
        fileService.getAllProjectsDirectories(dir.getPath(), projPaths, "test.txt");
        assertEquals(1, projPaths.size());
        fileInDir.delete();
        dir.delete();
    }

    @Test
    void folderCopy() throws IOException {
        if (!dir.exists()) {
            dir.mkdirs();
        }
        if (!fileInDir.exists()) {
            fileInDir.createNewFile();
        }
        fileService.folderCopy(dir.getPath(), dest.getPath());
        File fileInDest = new File(dest.getPath() + File.separator + "test.txt");
        assertTrue(fileInDest.exists());
        fileInDest.delete();
        dest.delete();
        fileInDir.delete();
        dir.delete();

    }

    @Test
    void startWith() {
        String a = "a/b/c";
        String b = "a/b";
        assertTrue(new FileServiceImp().startWith(a, b));
    }

    @Test
    void getRelativePath() {
        String a = "a/b/c";
        String b = "a/b";
        assertEquals("c", FileServiceImp.getRelativePath(a, b));
    }

    @Test
    void combinePath() {
        String a = "a";
        String b = "b";
        assertEquals("a/b", FileServiceImp.combinePath(a, b));
    }

    @Test
    void readSection() throws IOException {
        if (!readFile.exists()) {
            readFile.createNewFile();
        }
        fileService.fileUpdate(readFile.getPath(), "{\"a\":\"b\"}");
        assertEquals("b", FileServiceImp.readSection(readFile.getPath(), "a").asText());
        readFile.delete();
    }

    @Test
    void createDirAndFile() {
        if (fileInDir.exists()) {
            fileInDir.delete();
        }
        if (dir.exists()) {
            dir.delete();
        }
        FileServiceImp.createDirAndFile(fileInDir.getPath());
        assertTrue(fileInDir.exists());
        fileInDir.delete();
        dir.delete();
    }

    @Test
    void isEqual() {
        String a = "a";
        String b = "a";
        assertTrue(FileServiceImp.isEqual(a, b));
    }
}